/**
 * \file  ranker.cpp
 * \brief Definition of Ranker implementation class.
 *
 * LAV: Lib Anti Virals
 * <http://lav.googlecode.com/>
 * Copyright (C) 2013 Boretto Martin, Lenarduzzi Federico, FuDePAN
 *
 * This file is part of the LAV project.
 *
 * Contents:       Cpp file for LAV providing class Ranker implementation.
 *
 * System:         LAV
 * Language:       C++
 *
 * Author:         Boretto Martin
 * E-Mail:         martinboretto@gmail.com
 *
 * Author:         Lenarduzzi Federico
 * E-Mail:         federicolenarduzzi@gmail.com
 *
 * LAV is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LAV is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LAV.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "lav/antiviral_set.h"

namespace lav
{


// -----------------------
// Public methods section
// -----------------------


void AntiviralSet::Ranker::calculate_genetic_distance(const biopp::PseudoNucSequence& sequence,
        const biopp::DistanceMatrix& matrix,
        const ARVExpansionContainer& arvs_expansion,
        CombinationDistanceContainer& combinations_with_distances) const
{
    ARVExpansionContainer::const_iterator it_comb_exp;
    biopp::NucDistance current_distance;
    for (it_comb_exp = arvs_expansion.begin(); it_comb_exp != arvs_expansion.end(); ++it_comb_exp)
    {
        ARVExpansion::const_iterator it_second;
        ARVExpansion second = (*it_comb_exp);
        for (it_second = second.begin(); it_second != second.end(); ++it_second)
        {
            AminoacidExpansion::const_iterator it_first;
            AminoacidExpansion first = (*it_second);
            current_distance = 0;
            for (it_first = first.begin(); it_first != first.end(); ++it_first)
                // Calculates the total distance of the current combination
                calculate_current_genetic_distance(sequence, matrix, it_first, current_distance);

            // The result is ready to be inserted in the result
            CombinationDistance current_combination((*it_second), current_distance);
            mili::insert_into(combinations_with_distances, current_combination);
        }
    }
    // The result is sorted by genetic distance
    combinations_with_distances.sort();
}


void AntiviralSet::Ranker::rank_combinations(const CombinationDistanceContainer& combinations_with_distances,
        CombinationDistanceContainer& combinations_with_distances_reduce) const
{
    double min_distance;
    bool no_final;
    CombinationDistanceContainer::const_iterator it_comb_exp = combinations_with_distances.begin();
    min_distance = it_comb_exp->distance;
    do
    {
        mili::insert_into(combinations_with_distances_reduce, (*it_comb_exp));
        ++it_comb_exp;
        no_final = (it_comb_exp != combinations_with_distances.end());
    }
    while ((it_comb_exp->distance <= min_distance + TOLERANCE) && no_final);
}


// -----------------------
// Private methods section
// -----------------------

void AntiviralSet::Ranker::calculate_current_genetic_distance(const biopp::PseudoNucSequence& sequence,
        const biopp::DistanceMatrix& matrix,
        const AminoacidExpansion::const_iterator& it_first,
        biopp::NucDistance& distance_result) const
{
    biopp::PseudoTriplet pos_triplet;
    // Get the triplet correpondons to the current pos of the sequence
    pos_triplet = sequence.get_triplet(it_first->pos);
    // Calculates the total distance of the combination
    if (pos_triplet.isPureTriplet())
    {
        // The triplet obtained from the sequence, is a pure triplet
        biopp::Triplet current_pure_triplet(pos_triplet[0], pos_triplet[1], pos_triplet[2]);
        distance_result += it_first->triplet.distance(current_pure_triplet, matrix);
    }
    else
    {
        // The triplet obtained from the sequence, is a pseudotriplet
        biopp::TripletsIterator it_of_pure_triplets;
        //biopp::TripletsIterator::const_iterator it_triplets;
        biopp::NucDistance min_triplets_distance = 99;
        biopp::NucDistance aux_distance = 0;
        for (it_of_pure_triplets = pos_triplet.getPureTriplets(); !it_of_pure_triplets.end(); ++it_of_pure_triplets)
        {
            aux_distance = it_first->triplet.distance((*it_of_pure_triplets), matrix);
            if (aux_distance < min_triplets_distance)
                min_triplets_distance = aux_distance;
        }
        distance_result += min_triplets_distance;
    }
}
}